﻿
using AE_BEPUPhysics_Addition;
using Cysharp.Threading.Tasks;
using FixMath.NET;
using GameProtocol.Protocol;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Net;
using TEngine;
using UnityEngine;
using UnityEngine.Events;

namespace GameLogic
{
    public class FightClient : UDPClient
    {
        GameProtocol.FrameSyncData m_CurFrame = new GameProtocol.FrameSyncData();
        long m_LocalTimeStamp = 0;

        UnityEvent<GameProtocol.FrameSyncData> m_OnFrameUpdate = new UnityEvent<GameProtocol.FrameSyncData>();
        UnityEvent<GameProtocol.FrameSyncData> m_OnFrameSend = new UnityEvent<GameProtocol.FrameSyncData>();

        public uint PlayerID { get => m_ConversationID; set => m_ConversationID = value; }
        public GameProtocol.FrameSyncData CurFrame { get => m_CurFrame; set => m_CurFrame = value; }
        public UnityEvent<GameProtocol.FrameSyncData> OnFrameUpdate { get => m_OnFrameUpdate; set => m_OnFrameUpdate = value; }
        public UnityEvent<GameProtocol.FrameSyncData> OnFrameSend { get => m_OnFrameSend; set => m_OnFrameSend = value; }

        #region 公开接口
        public static FightClient Create()
        {
            FightClient client = new FightClient();
            client.AddListener(GameProtocol.Protocol.CSMsgId.SwitchShipRsp, client.OnSwitchShipRsp);
            client.AddListener(GameProtocol.Protocol.CSMsgId.StartGameNtf, client.OnStartGameNtf);
            client.AddListener(GameProtocol.Protocol.CSMsgId.FrameSyncRsp, client.OnFrameSyncRsp);
            client.AddListener(GameProtocol.Protocol.CSMsgId.DisconnectedNtf, client.OnPlayerDisconnectedNtf);
            return client;
        }
        public override void Disconnect()
        {
            base.Disconnect();  
            var dataContent = new DisconnectRqrData();
            Send(GameProtocol.Protocol.CSMsgId.DisconnectRqr, dataContent.ToByteArray());
        }

        object lockObj = new object();
        Queue<GameProtocol.FrameSyncData> m_ReceiveQueue = new Queue<GameProtocol.FrameSyncData>();
        protected bool m_IsStarted = false;
        protected long m_ServerLogicFrameCount = 0;
        protected long m_LocalLogicFrameCount = 0;
        protected long m_PreviousFrameTimeStamp = 0;  //  上一次纪录帧的时间戳

        long m_DelayTestLogicFrame = -1;
        float m_DelayTestTime = -1;
        public override void Update()
        {
            base.Update();
            if (!m_IsStarted)
                return;
            m_LocalTimeStamp = DateTime.UtcNow.Ticks;
            var deltaStamp = (long)(SystemModule.Instance.LogicDelta.ToFloat() * 10000000);
            if (m_PreviousFrameTimeStamp == 0)
                m_PreviousFrameTimeStamp = DateTime.UtcNow.Ticks + deltaStamp;
            //bool needPause = m_LocalLogicFrameCount > m_ServerLogicFrameCount + 14;
            //if(needPause)
            //    m_PreviousFrameTimeStamp = m_LocalTimeStamp ;
            while (m_LocalTimeStamp >= m_PreviousFrameTimeStamp + deltaStamp)
            {
                // 延迟测试
                if (m_DelayTestLogicFrame == -1)
                {
                    m_DelayTestLogicFrame = m_LocalLogicFrameCount;
                    m_DelayTestTime = Time.time;
                }

                m_CurFrame.LogicFrameID = m_LocalLogicFrameCount;
                m_CurFrame.PlayerID = m_ConversationID;
                m_CurFrame.TimeStamp = DateTime.UtcNow.Ticks;
                //m_CurFrame.TimeStamp = m_PreviousFrameTimeStamp;
                if (!m_CurFrame.PlayerDatas.ContainsKey(m_ConversationID))
                    m_CurFrame.PlayerDatas.Add(m_ConversationID, new GameProtocol.FramePlayerSyncData());
                m_OnFrameSend.Invoke(m_CurFrame);
                // 发消息给服务器
                var data = m_CurFrame.ToByteArray();
                Send(GameProtocol.Protocol.CSMsgId.FrameSyncRqr, data);
                Debug.Log("send frame to server: " + m_CurFrame.LogicFrameID);
                m_CurFrame = new GameProtocol.FrameSyncData();

                // 计算新的逻辑帧序号
                m_PreviousFrameTimeStamp += (long)((SystemModule.Instance.LogicDelta.ToFloat() + deltaStamp));
                m_LocalLogicFrameCount++;
            }

            lock (lockObj)
            {
                while (m_ReceiveQueue.Count > 0)
                {
                    var data = m_ReceiveQueue.Dequeue();
                }
            }
        }
        #endregion
        #region 保护接口
        #endregion
        #region 私有接口
        async void OnFrameSyncRsp(IPEndPoint addr, byte[] data)
        {
            var frameData = GameProtocol.FrameSyncData.Parser.ParseFrom(data);
            await UniTask.Yield();
            if (frameData != null && frameData.LogicFrameID >= 0)
            {
                if (frameData.LogicFrameID == m_DelayTestLogicFrame)
                {
                    m_DelayTestLogicFrame = -1;
                    Debug.Log("Frame sync delay = " + (Time.time - m_DelayTestTime) * 1000 + "ms");
                }
                m_OnFrameUpdate.Invoke(frameData);
                m_ServerLogicFrameCount++;
            }
        }

        async void OnSwitchShipRsp(IPEndPoint addr, byte[] data)
        {
            await UniTask.Yield();

            SwitchShipRspData joinGameRspData = SwitchShipRspData.Parser.ParseFrom(data);
            if (joinGameRspData != null)
            {
                var playerID = joinGameRspData.PlayerID;
                foreach (var i in joinGameRspData.AllShips)
                {
                    var ship = ShipModule.Instance.GetShip(i.Value.InstanceID);
                    if(ship == null)
                        ShipModule.Instance.CreateShip(i.Value);
                }
                if (joinGameRspData.JoinShip != null)
                {
                    var shipCfgID = joinGameRspData.JoinShip.CfgID;
                    var shipC = ShipModule.Instance.CreateShip(joinGameRspData.JoinShip);
                    FightLogicManager.Instance.SetPlayerControlShipInstanceID(playerID, shipC.ShipData.InstanceID);
                }
                Debug.Log("successed to join game, playerID = " + m_ConversationID);
            }
        }
        void OnStartGameNtf(IPEndPoint addr, byte[] data)
        {
            m_IsStarted = true;
            GameEvent.Send(GameEventType.OnOnlineGameStartClicked);
        }
        void OnPlayerDisconnectedNtf(IPEndPoint addr, byte[] data)
        {
            var disconnectNtfData =  DisconnectNtfData.Parser.ParseFrom(data);
            if (disconnectNtfData.ConversationID == m_ConversationID)
            {
                m_UDPClient.Close();
                m_UDPClient = null;
            }
            GameEvent.Send(GameEventType.OnPlayerDisconnected, disconnectNtfData.ConversationID);
            OnDisconnected();
        }
        #endregion
    }
}